从源码角度理解Can not perform this action after onSaveInstanceState异常

您所在的位置:网站首页 save save什么意思 从源码角度理解Can not perform this action after onSaveInstanceState异常

从源码角度理解Can not perform this action after onSaveInstanceState异常

#从源码角度理解Can not perform this action after onSaveInstanceState异常| 来源: 网络整理| 查看: 265

在开发中经常遇到Fatal Exception: java.lang.IllegalStateException:Can not perform this action after onSaveInstanceState异常,那这个异常出现原因是什么呢,怎么解决呢?

问题描述 出现Fatal Exception: java.lang.IllegalStateException:Can not perform this action after onSaveInstanceState异常有两种情况:

FragmentTransaction的commit()时出现: 具体堆栈信息如下:

Activity/FragmentActivity的onBackPressed时出现: 具体堆栈信息如下:

问题原因和解决方法 出现Fatal Exception: java.lang.IllegalStateException:Can not perform this action after onSaveInstanceState异常细分为两种情况,但产生原因是一样的,都是因为在存储状态之后调用了commit()/onBackPressed()方法,在commit()/onBackPrssed()中会调用checkStateLoss()方法,具体如下:

private void checkStateLoss() { if (this.mStateSaved) { throw new IllegalStateException("Can not perform this action after onSaveInstanceState"); } else if (this.mNoTransactionsBecause != null) { throw new IllegalStateException("Can not perform this action inside of " + this.mNoTransactionsBecause); } }

其中如果this.mStateSaved为true,就会抛出这个异常。而对于mStateSaved用了保存Fragment状态,是在Activity#onSaveInstanceState时通过调用FragmentManager#saveAllState方法,来进行Fragment的状态保存,同时设置mStateSaved为true,用来标识状态已被保存过。下面具体分析两种情况: 1.FragmentTransaction的commit()时出现: FragmentTransaction的commit()会调用BackStackRecord.java的commit()方法,可以看到commit()和commitAllowingStateLoss()的具体实现,如下: BackStackRecord.java

@Override public int commit() { return commitInternal(false); } @Override public int commitAllowingStateLoss() { return commitInternal(true); } int commitInternal(boolean allowStateLoss) { if (mCommitted) throw new IllegalStateException("commit already called"); if (FragmentManagerImpl.DEBUG) { Log.v(TAG, "Commit: " + this); LogWriter logw = new LogWriter(TAG); PrintWriter pw = new PrintWriter(logw); dump(" ", null, pw, null); pw.close(); } mCommitted = true; if (mAddToBackStack) { mIndex = mManager.allocBackStackIndex(this); } else { mIndex = -1; } mManager.enqueueAction(this, allowStateLoss); return mIndex; }

可以看出commit()和commitAllowingStateLoss()都是调用commitInternal()方法,只是传参不一样,而在commitInternal()方法中,根据传参会调用FragmentManager的enqueueAction方法,具体如下:

public void enqueueAction(OpGenerator action, boolean allowStateLoss) { if (!allowStateLoss) { checkStateLoss(); } synchronized (this) { if (mDestroyed || mHost == null) { throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { mPendingActions = new ArrayList(); } mPendingActions.add(action); scheduleCommit(); } }

如果allowStateLoss为true的话会调用checkStateLoss()方法进行检测。所以从源码可知commit()和commitAllowingStateLoss()的区别在于,commit()方法会检测fragment的状态,而commitAllowingStateLoss()不会对状态进行检测,状态会丢失。 由此得到结论和解决方法: (1)在activity的生命周期方法中提交事务要小心,越早越好,比如onCreate或是在接收用户的输入时来提交。尽量避免在onActivityResult()方法中提交。 (2)避免在异步的回调方法中执行commit。因为他们感知不到当前activity生命周期的状态。 (3)如果ui状态的改变对用户来说是可以接受的话使用commitAllowingStateLoss()代替commit()。 2.Activity/FragmentActivity的onBackPressed时出现: 按返回键时会调用super.onBackPressed()方法,如下: FragmentActivity.java中

@Override public void onBackPressed() { if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) { super.onBackPressed(); } }

会调用FragmentManagerImpl的popBackStackImmediate()方法,如下:

@Override public boolean popBackStackImmediate() { checkStateLoss(); return popBackStackImmediate(null, -1, 0); }

会调用checkStateLoss()方法,同上,会检测状态。 那这个问题怎么解决呢? (1)添加try…catch,感觉这种方法并没有解决根本问题,不推荐。 (2)重写onSaveInstanceState方法,不调用super,但onSaveInstanceState方法是用来存储状态使用的,不调用super,防止系统保存fragment的状态,可能会引发一引起其他的问题;再有就是,对于support包下,在其onStop时也会把mStateSaved置为true,仍然能够遇到state loss exception。不推荐。 (3)设置标志位,状态保存过后,不处理KEY事件。具体如下:

public class FragmentStateLossActivity extends Activity { private static final String TAG = "Fragment state loss"; private boolean mStateSaved; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fragment_state_loss); mStateSaved = false; } @Override protected void onSaveInstanceState(Bundle outState) { // Not call super won't help us, still get crash super.onSaveInstanceState(outState); imStateSaved = true; } @Override protected void onResume() { super.onResume(); mStateSaved = false; } @Override protected void onStop() { super.onStop(); mStateSaved = true; } @Override protected void onStart() { super.onStart(); mStateSaved = false; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (!mStateSaved) { return super.onKeyDown(keyCode, event); } else { // State already saved, so ignore the event return true; } } @Override public void onBackPressed() { if (!mStateSaved) { super.onBackPressed(); } } }

但这种方法虽然能从根本上解决crash,但相对比较麻烦。 目前还没有发现更好的方法。

参考 https://juejin.im/entry/58636864128fe10069efc4b5 https://huxian99.github.io/2016/08/28/cj3qymo360000owxk9zp17alo/



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3